import { mergeFile, uploadChunk, vertifyFile } from '@/api/file';
interface FileResult {
  code: number;
  data: {
    filePath: string;
  };
}
export interface IUploadChunkParams {
  // 文件分片
  chunk: ArrayBuffer;
  // 当前分片序号
  chunkIndex: number;
  // 文件Hash
  fileHash: string;
  // 文件后缀名
  extname: string;
}

export async function uploadFile(
  file: File, // 文件对象
  baseChunkSize: number, // 基础分块大小，默认为1MB
  maxRetries: number = 3, // 最大重试次数，默认为3次
  retryDelay: number = 1000, // 重试延迟时间，默认为1秒
  process_cb?: (progress: number) => void, // 进度回调函数
) {
  return new Promise((resolve, reject) => {
    const chunkList: ArrayBuffer[] = [];
    let fileHash = ''; // 文件哈希值
    // 创建文件分片
    const sliceFileWorker = new Worker(
      new URL('../worker/sliceFileWorker.ts', import.meta.url),
      {
        type: 'module',
      },
    );
    console.time('sliceFileWorker');
    sliceFileWorker.postMessage({ targetFile: file, baseChunkSize });
    sliceFileWorker.onmessage = async (e) => {
      console.timeEnd('sliceFileWorker');
      console.log(e);

      switch (e.data.messageType) {
        case 'success':
          chunkList.push(...e.data.chunks);
          fileHash = e.data.fileHash;
          console.log(chunkList, fileHash, '分片完成');
          try {
            // 上传文件分片
            const result = await handleFile(
              file,
              chunkList,
              fileHash,
              maxRetries,
              retryDelay,
              process_cb,
            );
            if (result.success) {
              resolve(result);
            } else {
              reject({ success: false, message: result.message });
            }
          } catch (error) {
            reject({ success: false, message: error });
          }
          break;
        case 'process':
          chunkList.push(...e.data.chunks);
          break;
        case 'fail':
          console.log(e.data.message || '分片失败');
          break;
        default:
          break;
      }
    };
  });
}

async function handleFile(
  file: File, // 文件对象
  chunkList: ArrayBuffer[], // 文件分片列表
  fileHash: string, // 文件哈希值
  maxRetries: number, // 最大重试次数
  retryDelay: number, // 重试延迟时间
  process_cb?: (progress: number) => void, // 进度回调函数
) {
  const filename = file.name;
  const extname = filename.split('.')[1];

  const allChunkList = chunkList;

  const neededChunkList: number[] = [];

  let progress = 0; // 进度
  try {
    const params = {
      totalCount: allChunkList.length, // 文件总片数
      extname, // 文件扩展名
      fileHash, // 文件哈希值
    };
    const result = (await vertifyFile(params)) as unknown as FileResult; // 上传文件分片
    console.log('result', result, result.data);
    if (result.code === 200) {
      const { needFileList, message } = result.data;
      console.log(needFileList, 'needFileList');
      if (!needFileList.length) {
        return {
          success: true,
          filePath: result.data.filePath,
          message: message || '',
        };
      }
      // 部分上传成功，更新neededChunkList，断点续传
      neededChunkList.push(...needFileList);
      console.log(neededChunkList, 'neededChunkList');
    } else if (result.code === 201) {
      // 文件已存在
      return {
        success: true,
        message: '上传成功',
        filePath: result.data.filePath,
      };
    } else if (result.code === 202) {
      // 文件全部上传成功

      // 已上传完成所有分片文件 请求合并
      const mergeParams = {
        fileHash,
        extname,
      };

      try {
        const mergeResult = (await mergeFile(
          mergeParams,
        )) as unknown as FileResult;
        if (mergeResult.code === 200) {
          return {
            success: true,
            message: '上传成功',
            filePath: mergeResult.data.filePath,
          };
        } else {
          throw new Error('合并文件失败');
        }
      } catch {
        throw new Error('合并文件失败');
      }
    }
  } catch {
    throw new Error('获取文件上传失败');
  }
  progress =
    ((allChunkList.length - neededChunkList.length) / allChunkList.length) *
    100;
  if (!allChunkList.length) {
    throw new Error('文件分片失败');
  }

  const requestList = allChunkList.map(
    async (chunk: ArrayBuffer, index: number) => {
      if (neededChunkList.includes(index + 1)) {
        const params = {
          chunk,
          chunkIndex: index + 1,
          extname,
          fileHash,
        };
        try {
          await uploadChunkWithRetry(params, maxRetries, retryDelay);
          progress += Math.ceil(100 / allChunkList.length);
          if (progress >= 100) {
            progress = 100;
          }
          if (progress >= 100) progress = 100;
          if (process_cb) process_cb(progress);
        } catch (error) {
          console.log(error);
          throw new Error('文件上传失败');
        }
        return;
      }
    },
  );

  try {
    await Promise.all(requestList);

    try {
      const params = {
        fileHash,
        extname,
      };
      const mergeResult = (await mergeFile(params)) as unknown as FileResult;
      if (mergeResult.code === 200) {
        return {
          success: true,
          message: '上传成功',
          filePath: mergeResult.data.filePath,
        };
      } else {
        throw new Error('合并文件失败');
      }
    } catch {
      throw new Error('合并文件失败');
    }
  } catch {
    throw new Error('文件上传失败');
  }
}

// 文件分片
const uploadChunkWithRetry = async (
  params: IUploadChunkParams,
  maxRetries: number = 3,
  retryDelay = 1000,
) => {
  let retries = 0;
  while (retries < maxRetries) {
    try {
      const res = await uploadChunk(params);
      console.log(res, 'res---data');
      if (res.code === 200) {
        return res;
      } else {
        throw new Error('文件上传失败');
      }
    } catch {
      retries++;
      if (retries >= maxRetries) {
        throw new Error('文件上传失败');
      }
      await new Promise((resolve) => setTimeout(resolve, retryDelay));
    }
  }
};
